﻿using System;
using JXT.PrimaryKey.Batman.Reflection;

namespace JXT.PrimaryKey.Batman.Domain.Models
{
    public static class ModelExtensions
    {
        public static T ToDomainModel<T>(this object model)
            where T : IEntity, new()
        {
            if (model == null)
                throw new ArgumentNullException("model");
            T result = new T();
            var properties = typeof(T).GetMetadataCacheItems();
            var thisPropertyDictionary = model.GetType().GetMetadataCacheDictionary();
            foreach (var item in properties)
            {
                //判断，只有普通可写属性可以顺利转换
                if (item.Property.Property.CanWrite &&
                    !item.Property.Property.PropertyType.IsEntityType() &&
                    !item.Property.Property.PropertyType.IsValueObjectType())
                {
                    LambdaReflectionMetadate metadata;
                    if (thisPropertyDictionary.TryGetValue(item.Name, out metadata))
                    {
                        if (item.Property.Property.PropertyType == metadata.Property.Property.PropertyType)
                        {
                            var value = metadata.Property.Get(model);
                            if (value != null)
                            {
                                item.Property.Set(result, Convert.ChangeType(value, item.Property.Property.PropertyType));
                            }
                        }
                    }
                }
            }
            return result;
        }

        public static void FromDomainModel<T>(this T model, IEntity entity) where T : class
        {
            if (model == null)
                throw new ArgumentNullException("model");
            var properties = model.GetMetadataCacheItems();
            var entityPropertyDictionary = entity.GetType().GetMetadataCacheDictionary();
            foreach (var item in properties)
            {
                if (item.Property.Property.CanWrite)
                {
                    LambdaReflectionMetadate metadata;
                    if (entityPropertyDictionary.TryGetValue(item.Name, out metadata))
                    {
                        if (item.Property.Property.PropertyType == metadata.Property.Property.PropertyType)
                        {
                            //Entity和ValueObject不能直接转换
                            if (!metadata.Property.Property.PropertyType.IsEntityType() &&
                                !metadata.Property.Property.PropertyType.IsValueObjectType())
                            {
                                var value = metadata.Property.Get(entity);
                                if (value != null)
                                {
                                    item.Property.Set(model, value);
                                }
                            }
                        }
                    }
                }
            }
        }

        public static bool IsEntityType(this Type type)
        {
            return typeof(IEntity).IsAssignableFrom(type);
        }

        public static bool IsValueObjectType(this Type type)
        {
            return typeof(IValueObject).IsAssignableFrom(type);
        }

        public static bool IsEntityOrValueObjectType(this Type type)
        {
            return type.IsEntityType() || type.IsValueObjectType();
        }

        public static bool IsEntity(this object obj)
        {
            if (obj == null)
                return false;
            return obj.GetType().IsEntityType();
        }

        public static bool IsValueObject(this object obj)
        {
            if (obj == null)
                return false;
            return obj.GetType().IsValueObjectType();
        }

        public static bool IsEntityOrValueObject(this object obj)
        {
            if (obj == null)
                return false;
            return obj.IsEntity() || obj.IsValueObject();
        }

        public static string GetEntityIdentityName(this Type type)
        {
            if (type.IsEntityType())
                return EntityBase<int>.IdentityName;
            return null;
        }
    }
}
